// src/main/java/com/fundcrawler/service/FundCrawlerService.java

package com.fundcrawler.service;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fundcrawler.common.Constants;
import com.fundcrawler.entity.BizParam;
import com.fundcrawler.entity.FundNav;
import com.fundcrawler.service.manager.BizParamManager;
import com.fundcrawler.service.manager.FundNavManager;
import lombok.extern.slf4j.Slf4j;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
public class FundCrawlerAspxService {

    // 天天基金网历史净值接口
    private static final String API_URL = "https://fundf10.eastmoney.com/F10DataApi.aspx";

    @Autowired
    private FundNavManager fundNavManager;
    @Autowired
    private BizParamManager bizParamManager;
    private OkHttpClient client = new OkHttpClient.Builder()
            .connectTimeout(30, TimeUnit.SECONDS)
            .readTimeout(30, TimeUnit.SECONDS)
            .build();
    private ObjectMapper objectMapper=new ObjectMapper();


    public void crawlFundHistory(String fundCode) throws Exception {
        int page = 1;
        BizParam bizParam = bizParamManager.findByParamCode(Constants.ASPX_SIZE_CODE);
        int perPage = Integer.valueOf(bizParam.getParamValue());
        String fundName = bizParamManager.findByParamCodeGroupDefault(Constants.FUND_NAME, fundCode, fundCode);
        FundNav latestByFundCode = fundNavManager.findLatestByFundCode(fundCode);
        String startDate=latestByFundCode==null?""
                :latestByFundCode.getDate().format(DateTimeFormatter.ISO_LOCAL_DATE);
        boolean hasMore = true;

        while (hasMore) {
            String url = buildUrl(fundCode, page, perPage, startDate);
            log.info("请求第 {} 页: {}", page, url);

            try (Response response = client.newCall(createRequest(url, fundCode)).execute()) {
                if (!response.isSuccessful()) {
                    log.error("HTTP 请求失败: 状态码 = {}", response.code());
                    throw new RuntimeException("HTTP error code: " + response.code());
                }

                String body = response.body().string();
                log.info("返回内容：{}", body);
                // 🔥 1. 提取 JavaScript 变量中的 JSON 部分
                String jsonStr = extractJsonFromVar(body);
                if (jsonStr == null) {
                    log.error("无法提取 JSON 数据");
                    break;
                }

                // 🔥 2. 解析 JSON 获取 content 和分页信息
                JsonNode rootNode = objectMapper.readTree(jsonStr);
                String tableHtml = rootNode.get("content").asText();
                int totalPages = rootNode.get("pages").asInt();
                int curPage = rootNode.get("curpage").asInt();

                // 🔥 3. 使用 Jsoup 解析 HTML 表格
                List<FundNav> navList = parseTableHtml(fundCode, fundName, tableHtml);
                if (navList.isEmpty()) {
                    log.info("第 {} 页无数据，停止", page);
                    hasMore = false;
                    break;
                }
                List<FundNav> insertList = navList.stream()
                        .filter(e -> latestByFundCode == null || e.getDate().isAfter(latestByFundCode.getDate()))
                        .collect(Collectors.toList());
                List<FundNav> updList = navList.stream()
                        .filter(e -> latestByFundCode != null && !e.getDate().isAfter(latestByFundCode.getDate()))
                        .collect(Collectors.toList());

                // 🔥 4. 保存到数据库
                fundNavManager.saveAll(insertList, updList);
                log.info("✅ 基金 {} 第 {} 页，保存 {} 条记录", fundCode, curPage, navList.size());

                if (page >= totalPages) {
                    hasMore = false;
                }

                page++;
                Thread.sleep(1500); // 防反爬

            } catch (Exception e) {
                log.error("爬取第 {} 页失败: {}", page, e.getMessage(), e);
                break;
            }
        }

        log.info("🎉 基金 {} 历史净值爬取完成", fundCode);
    }

    private String buildUrl(String fundCode, int page, int perPage, String startDate) {
        return String.format(
                "%s?type=lsjz&code=%s&page=%d&per=%d&sdate=%s",
                API_URL, fundCode, page, perPage,startDate
        );
    }

    private Request createRequest(String url, String fundCode) {
        return new Request.Builder()
                .url(url)
                .header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36")
                .header("Referer", "https://fundf10.eastmoney.com/jjjz_" + fundCode + ".html")
                .header("Accept", "*/*")
                .header("X-Requested-With", "XMLHttpRequest")
                .build();
    }

    // 🔥 1. 从 var apidata = { ... }; 中提取 { ... }
    private String extractJsonFromVar(String js) {
        js = js.trim();
        int start = js.indexOf('{');
        int end = js.lastIndexOf('}');
        if (start >= 0 && end > start) {
            String subStr = js.substring(start, end + 1);
            subStr=subStr.replaceAll("\\{\\s*content\\s*:","{\"content\":")
                    .replaceAll("\",\\s*records\\s*:","\",\"records\":")
                    .replaceAll(",\\s*pages\\s*:",",\"pages\":")
                    .replaceAll(",\\s*curpage\\s*:",",\"curpage\":")
            ;
            return subStr;
        }
        return null;
    }

    // 🔥 2. 使用 Jsoup 解析 HTML 表格
    private List<FundNav> parseTableHtml(String fundCode, String fundname, String tableHtml) {
        List<FundNav> result = new ArrayList<>();
        Document doc = Jsoup.parse(tableHtml);
        Elements rows = doc.select("tbody tr");

        for (Element row : rows) {
            Elements cells = row.select("td");
            if (cells.size() < 6) continue;

            try {
                FundNav nav = new FundNav();
                nav.setFundCode(fundCode);
                nav.setFundName(fundname);
                nav.setDate(LocalDate.parse(cells.get(0).text().trim()));

                safeSetBigDecimal(cells.get(1).text(), nav::setUnitNav);
                safeSetBigDecimal(cells.get(2).text(), nav::setAccumulatedNav);
                safeSetBigDecimal(cells.get(3).text().replace("%", ""), nav::setDailyGrowth);

                nav.setPurchaseStatus(cells.get(4).text().trim());
                nav.setRedemptionStatus(cells.get(5).text().trim());
                nav.setDividend(cells.size() > 6 ? cells.get(6).text().trim() : "");

                result.add(nav);
            } catch (Exception e) {
                log.warn("解析某行数据失败: {}", row.text());
            }
        }

        return result;
    }

    private void safeSetBigDecimal(String value, ThrowingConsumer<BigDecimal> setter) {
        if (value != null && !value.isEmpty() && !"-".equals(value)) {
            try {
                setter.accept(new BigDecimal(value));
            } catch (Exception e) {
                log.warn("数值转换失败: {}", value);
            }
        }
    }

    @FunctionalInterface
    interface ThrowingConsumer<T> {
        void accept(T t) throws Exception;
    }

}
